<?php
/***********************************************************************
 * @package     IvanEngine
 * @subpackage  Core
 * @author      Ivan <ivanzx@msn.com>
 * @created     2010-07-17
 * @Purpose     
 ***********************************************************************/
if(defined("IVANENGINE_CORE_FORMHELPER"))
    die("Redefinition macro in :".__FILE__."(".__LINE__.")");
define("IVANENGINE_CORE_FORMHELPER", true);

require_once (dirname(__FILE__).'/../Common.php');

class CFormHelper extends CBase
{
    public function __construct()
    {
        parent::__construct();
    }
    
    public function __destruct()
    {
        parent::__destruct();
    }
    
    /**
    * Returns a formatted set of <option> tags based on optional <i>$options</i> array variable.
    *
    * The self::OptionsForSelect helper is usually called in conjunction with the SelectTag helper, as it is relatively
    * useless on its own. By passing an array of <i>$options</i>, the helper will automatically generate <option> tags
    * using the array key as the value and the array value as the display title. Additionally the self::OptionsForSelect CTagHelper::Tag is
    * smart enough to detect nested arrays as <optgroup> tags.  If the helper detects that the array value is an array itself,
    * it creates an <optgroup> CTagHelper::Tag with the name of the group being the key and the contents of the <optgroup> being the array.
    *
    * <b>Options:</b>
    * - include_blank  - Includes a blank <option> CTagHelper::Tag at the beginning of the string with an empty value
    * - include_custom - Includes an <option> CTagHelper::Tag with a custom display title at the beginning of the string with an empty value
    *
    * <b>Examples:</b>
    * <code>
    *  echo SelectTag('person', self::OptionsForSelect(array(1 => 'Larry', 2 => 'Moe', 3 => 'Curly')));
    * </code>
    *
    * <code>
    *  $card_list = array('VISA' => 'Visa', 'MAST' => 'MasterCard', 'AMEX' => 'American Express', 'DISC' => 'Discover');
    *  echo SelectTag('cc_type', self::OptionsForSelect($card_list, 'AMEX', array('include_custom' => '-- Select Credit Card Type --')));
    * </code>
    *
    * <code>
    *  $optgroup_array = array(1 => 'Joe', 2 => 'Sue', 'Group A' => array(3 => 'Mary', 4 => 'Tom'), 'Group B' => array(5 => 'Bill', 6 =>'Andy'));
    *  echo SelectTag('employee', self::OptionsForSelect($optgroup_array, null, array('include_blank' => true)), array('class' => 'mystyle'));
    * </code>
    *
    * @param array  $options      dataset to create <option> tags and <optgroup> tags from
    * @param string $selected     selected option value
    * @param array  $html_options additional HTML compliant <option> CTagHelper::Tag parameters
    *
    * @return string populated with <option> tags derived from the <i>$options</i> array variable
    * @see SelectTag
    */
    public static public function OptionsForSelect($options = array(), $selected = '', $html_options = array())
    {
        $html_options = CTagHelper::_ParseAttributes($html_options);

        if (!is_array($selected))
        {
            $selected = array($selected);
        }

        $selected = array_map('strval', array_values($selected));
        $selected_set = array_flip($selected);

        $html = '';

        if ($value = CTagHelper::_GetOption($html_options, 'include_custom'))
        {
            $html .= CTagHelper::ContentTag('option', $value, array('value' => ''))."\n";
        }
        else if (CTagHelper::_GetOption($html_options, 'include_blank'))
        {
            $html .= CTagHelper::ContentTag('option', '', array('value' => ''))."\n";
        }

        foreach ($options as $key => $value)
        {
            if (is_array($value) || $value instanceof sfOutputEscaperArrayDecorator)
            {
                $html .= CTagHelper::ContentTag('optgroup', self::OptionsForSelect($value, $selected, $html_options), array('label' => $key))."\n";
            }
            else
            {
                $option_options = array('value' => $key);

                if (isset($selected_set[strval($key)])) {
                    $option_options['selected'] = 'selected';
                }

                $html .= CTagHelper::ContentTag('option', $value, $option_options)."\n";
            }
        }

        return $html;
    }

    /**
    * Returns an HTML <form> CTagHelper::Tag that points to a valid action, route or URL as defined by <i>$url_for_options</i>.
    *
    * By default, the form CTagHelper::Tag is generated in POST format, but can easily be configured along with any additional
    * HTML parameters via the optional <i>$options</i> parameter. If you are using file uploads, be sure to set the 
    * <i>multipart</i> option to true.
    *
    * <b>Options:</b>
    * - multipart - When set to true, enctype is set to "multipart/form-data".
    *
    * <b>Examples:</b>
    *   <code><?php echo FormTag('@myroute'); ?></code>
    *   <code><?php echo FormTag('/plugin/action', array('name' => 'myformname', 'multipart' => true)); ?></code>
    *
    * @param  string $url_for_options  valid action, route or URL
    * @param  array  $options          optional HTML parameters for the <form> CTagHelper::Tag
    *
    * @return string opening HTML <form> CTagHelper::Tag with options
    */
    static public function FormTag($url_for_options = '', $options = array())
    {
        $options = CTagHelper::_ParseAttributes($options);

        $html_options = $options;

        $html_options['method'] = isset($html_options['method']) ? strtolower($html_options['method']) : 'post';

        if (CTagHelper::_GetOption($html_options, 'multipart'))
        {
            $html_options['enctype'] = 'multipart/form-data';
        }

        $html_options['action'] = CUrlHelper::UrlFor($url_for_options);

        $html = '';
        if (!in_array($html_options['method'], array('get', 'post')))
        {
            $html = CTagHelper::Tag('input', array('type' => 'hidden', 'name' => 'sf_method', 'value' => $html_options['method']));
            $html_options['method'] = 'post';
        }

        return CTagHelper::Tag('form', $html_options, true).$html;
    }

    /**
    * Returns a <select> CTagHelper::Tag, optionally comprised of <option> tags.
    *
    * The select CTagHelper::Tag does not generate <option> tags by default.  
    * To do so, you must populate the <i>$option_tags</i> parameter with a string of valid HTML compliant <option> tags.
    * Fortunately, Symfony provides a handy helper function to convert an array of data into option tags (see self::OptionsForSelect). 
    * If you need to create a "multiple" select CTagHelper::Tag (ability to select multiple options), set the <i>multiple</i> option to true.  
    * Doing so will automatically convert the name field to an array type variable (i.e. name="name" becomes name="name[]").
    * 
    * <b>Options:</b>
    * - multiple - If set to true, the select CTagHelper::Tag will allow multiple options to be selected at once.
    *
    * <b>Examples:</b>
    * <code>
    *  $person_list = array(1 => 'Larry', 2 => 'Moe', 3 => 'Curly');
    *  echo SelectTag('person', self::OptionsForSelect($person_list, $sf_params->get('person')), array('class' => 'full'));
    * </code>
    *
    * <code>
    *  echo SelectTag('department', self::OptionsForSelect($department_list), array('multiple' => true));
    * </code>
    *
    * <code>
    *  echo SelectTag('url', self::OptionsForSelect($url_list), array('onChange' => 'Javascript:this.form.submit();'));
    * </code>
    *
    * @param  string $name         field name 
    * @param  mixed  $option_tags  contains a string of valid <option></option> tags, or an array of options that will be passed to self::OptionsForSelect
    * @param  array  $options      additional HTML compliant <select> CTagHelper::Tag parameters
    *
    * @return string <select> CTagHelper::Tag optionally comprised of <option> tags.
    * @see self::OptionsForSelect, CTagHelper::ContentTag
    */
    static public function SelectTag($name, $option_tags = null, $options = array())
    {
        $options = CTagHelper::_ConvertOptions($options);
        $id = $name;
        if (isset($options['multiple']) && $options['multiple'] && substr($name, -2) !== '[]')
        {
            $name .= '[]';
        }
        if (is_array($option_tags))
        {
            $option_tags = self::OptionsForSelect($option_tags);
        }

        return CTagHelper::ContentTag('select', $option_tags, array_merge(array('name' => $name, 'id' => CTagHelper::GetIdFromName($id)), $options));
    }

    /**
    * Returns an XHTML compliant <input> CTagHelper::Tag with type="text".
    *
    * The self::InputTag helper generates your basic XHTML <input> CTagHelper::Tag and can utilize any standard <input> CTagHelper::Tag parameters 
    * passed in the optional <i>$options</i> parameter.
    *
    * <b>Examples:</b>
    * <code>
    *  echo self::InputTag('name');
    * </code>
    *
    * <code>
    *  echo self::InputTag('amount', $sf_params->get('amount'), array('size' => 8, 'maxlength' => 8));
    * </code>
    *
    * @param  string $name     field name 
    * @param  string $value    selected field value
    * @param  array  $options  additional HTML compliant <input> CTagHelper::Tag parameters
    *
    * @return string XHTML compliant <input> CTagHelper::Tag with type="text"
    */
    static public function InputTag($name, $value = null, $options = array())
    {
        return CTagHelper::Tag('input', array_merge(array('type' => 'text', 'name' => $name, 'id' => CTagHelper::GetIdFromName($name, $value), 'value' => $value), CTagHelper::_ConvertOptions($options)));
    }

    /**
    * Returns an XHTML compliant <input> CTagHelper::Tag with type="hidden".
    *
    * Similar to the self::InputTag helper, the self::InputHiddenTag helper generates an XHTML <input> CTagHelper::Tag and can utilize 
    * any standard <input> CTagHelper::Tag parameters passed in the optional <i>$options</i> parameter.  The only difference is 
    * that it creates the CTagHelper::Tag with type="hidden", meaning that is not visible on the page.
    *
    * <b>Examples:</b>
    * <code>
    *  echo self::InputHiddenTag('id', $id);
    * </code>
    *
    * @param  string $name     field name 
    * @param  string $value    populated field value
    * @param  array  $options  additional HTML compliant <input> CTagHelper::Tag parameters
    *
    * @return string XHTML compliant <input> CTagHelper::Tag with type="hidden"
    */
    static public function InputHiddenTag($name, $value = null, $options = array())
    {
        $options = CTagHelper::_ParseAttributes($options);

        $options['type'] = 'hidden';
        return self::InputTag($name, $value, $options);
    }

    /**
    * Returns an XHTML compliant <input> CTagHelper::Tag with type="file".
    *
    * Similar to the self::InputTag helper, the self::InputHiddenTag helper generates your basic XHTML <input> CTagHelper::Tag and can utilize
    * any standard <input> CTagHelper::Tag parameters passed in the optional <i>$options</i> parameter.  The only difference is that it 
    * creates the CTagHelper::Tag with type="file", meaning that next to the field will be a "browse" (or similar) button. 
    * This gives the user the ability to choose a file from there computer to upload to the web server.  Remember, if you 
    * plan to upload files to your website, be sure to set the <i>multipart</i> option FormTag helper function to true 
    * or your files will not be properly uploaded to the web server.
    *
    * <b>Examples:</b>
    * <code>
    *  echo self::InputFileTag('filename', array('size' => 30));
    * </code>
    *
    * @param string $name    field name 
    * @param array  $options additional HTML compliant <input> CTagHelper::Tag parameters
    *
    * @return string XHTML compliant <input> CTagHelper::Tag with type="file"
    * @see self::InputTag, FormTag
    */
    static public function InputFileTag($name, $options = array())
    {
        $options = CTagHelper::_ParseAttributes($options);

        $options['type'] = 'file';
        return self::InputTag($name, null, $options);
    }

    /**
    * Returns an XHTML compliant <input> CTagHelper::Tag with type="password".
    *
    * Similar to the self::InputTag helper, the self::InputHiddenTag helper generates your basic XHTML <input> CTagHelper::Tag and can utilize
    * any standard <input> CTagHelper::Tag parameters passed in the optional <i>$options</i> parameter.  The only difference is that it 
    * creates the CTagHelper::Tag with type="password", meaning that the text entered into this field will not be visible to the end user.
    * In most cases it is replaced by  * * * * * * * *.  Even though this text is not readable, it is recommended that you do not 
    * populate the optional <i>$value</i> option with a plain-text password or any other sensitive information, as this is a 
    * potential security risk.
    *
    * <b>Examples:</b>
    * <code>
    *  echo self::InputPasswordTag('password');
    *  echo self::InputPasswordTag('password_confirm');
    * </code>
    *
    * @param string $name    field name
    * @param string $value   populated field value
    * @param array  $options additional HTML compliant <input> CTagHelper::Tag parameters
    *
    * @return string XHTML compliant <input> CTagHelper::Tag with type="password"
    * @see self::InputTag
    */
    static public function InputPasswordTag($name = 'password', $value = null, $options = array())
    {
        $options = CTagHelper::_ParseAttributes($options);

        $options['type'] = 'password';
        return self::InputTag($name, $value, $options);
    }

    /**
    * Returns a <textarea> CTagHelper::Tag, optionally wrapped with an inline rich-text JavaScript editor.
    *
    * The texarea_tag helper generates a standard HTML <textarea> CTagHelper::Tag and can be manipulated with
    * any number of standard HTML parameters via the <i>$options</i> array variable.  However, the 
    * textarea CTagHelper::Tag also has the unique capability of being transformed into a WYSIWYG rich-text editor
    * such as TinyMCE (http://tinymce.moxiecode.com) very easily with the use of some specific options:
    *
    * <b>Options:</b>
    *  - rich: A rich text editor class (for example sfRichTextEditorTinyMCE for TinyMCE).
    *
    * <b>Examples:</b>
    * <code>
    *  echo self::TextareaTag('notes');
    * </code>
    *
    * <code>
    *  echo self::TextareaTag('description', 'This is a description', array('rows' => 10, 'cols' => 50));
    * </code> 
    *
    * @param  string $name     field name
    * @param  string $content  populated field value
    * @param  array  $options  additional HTML compliant <textarea> CTagHelper::Tag parameters
    *
    * @return string <textarea> CTagHelper::Tag optionally wrapped with a rich-text WYSIWYG editor
    */
    static public function TextareaTag($name, $content = null, $options = array())
    {
        $options = CTagHelper::_ParseAttributes($options);

        if ($size = CTagHelper::_GetOption($options, 'size'))
        {
            list($options['cols'], $options['rows']) = explode('x', $size, 2);
        }

        // rich control?
        if ($rich = CTagHelper::_GetOption($options, 'rich', false))
        {
            // switch for backward compatibility
            switch ($rich)
            {
              case 'tinymce':
                $rich = 'TinyMCE';
                break;
              case 'fck':
                $rich = 'FCK';
                break;
            }

            $editorClass = 'CRichTextEditor'.$rich;

            if (!class_exists($editorClass))
            {
                throw new CException(sprintf('The rich text editor "%s" does not exist.', $editorClass));
            }

            $editor = new $editorClass();
            if (!in_array('CRichTextEditor', class_parents($sfEditor)))
            {
                throw new CException(sprintf('The editor "%s" must extend sfRichTextEditor.', $editorClass));
            }
            $editor->Initialize($name, $content, $options);

            return $editor->ToHTML();
        }

        return CTagHelper::ContentTag('textarea', 
            CTagHelper::EscapeOnce((is_object($content)) ? $content->__toString() : $content), 
            array_merge(array('name' => $name, 
                'id' => CTagHelper::GetIdFromName(
                    CTagHelper::_GetOption($options, 'id', $name), null)), 
                CTagHelper::_ConvertOptions($options)
            ));
    }

    /**
    * Returns an XHTML compliant <input> CTagHelper::Tag with type="checkbox".
    *
    * When creating multiple checkboxes with the same name, be sure to use an array for the
    * <i>$name</i> parameter (i.e. 'name[]').  The self::CheckboxTag is smart enough to create unique ID's
    * based on the <i>$value</i> parameter like so:
    *
    * <samp>
    *  <input type="checkbox" name="status[]" id="status_3" value="3" />
    *  <input type="checkbox" name="status[]" id="status_4" value="4" />
    * </samp>
    * 
    * <b>Examples:</b>
    * <code>
    *  echo self::CheckboxTag('newsletter', 1, $sf_params->get('newsletter'));
    * </code>
    *
    * <code>
    *  echo self::CheckboxTag('option_a', 'yes', true, array('class' => 'style_a'));
    * </code>
    *
    * <code>
    *  // one request variable with an array of checkbox values
    *  echo self::CheckboxTag('choice[]', 1);
    *  echo self::CheckboxTag('choice[]', 2);
    *  echo self::CheckboxTag('choice[]', 3);
    *  echo self::CheckboxTag('choice[]', 4); 
    * </code>
    *
    * <code>
    *  // assuming you have Prototype.js enabled, you could do this
    *  echo self::CheckboxTag('show_tos', 1, false, array('onclick' => "Element.toggle('tos'); return false;"));
    * </code>
    *
    * @param  string $name     field name 
    * @param  string $value    checkbox value (if checked)
    * @param  bool   $checked  is the checkbox checked? (1 or 0)
    * @param  array  $options  additional HTML compliant <input> CTagHelper::Tag parameters
    *
    * @return string XHTML compliant <input> CTagHelper::Tag with type="checkbox"
    */
    static public function CheckboxTag($name, $value = '1', $checked = false, $options = array())
    {
        $html_options = array_merge(
            array(
                'type' => 'checkbox', 
                'name' => $name, 
                'id' => CTagHelper::GetIdFromName($name, $value), 
                'value' => $value
            ), 
            CTagHelper::_ConvertOptions($options));

        if ($checked)
        {
            $html_options['checked'] = 'checked';
        }

        return CTagHelper::Tag('input', $html_options);
    }

    /**
    * Returns an XHTML compliant <input> CTagHelper::Tag with type="radio".
    *
    * <b>Examples:</b>
    * <code>
    *  echo ' Yes '.self::RadiobuttonTag('newsletter', 1);
    *  echo ' No '.self::RadiobuttonTag('newsletter', 0); 
    * </code>
    *
    * @param  string $name     field name 
    * @param  string $value    radio button value (if selected)
    * @param  bool   $checked  is the radio button selected? (1 or 0)
    * @param  array  $options  additional HTML compliant <input> CTagHelper::Tag parameters
    *
    * @return string XHTML compliant <input> CTagHelper::Tag with type="radio"
    */
    static public function RadioButtonTag($name, $value, $checked = false, $options = array())
    {
        $html_options = array_merge(
            array(
                'type' => 'radio', 
                'name' => $name, 
                'id' => CTagHelper::GetIdFromName($name.'[]', $value), 
                'value' => $value
            ), 
            CTagHelper::_ConvertOptions($options));

        if ($checked)
        {
            $html_options['checked'] = 'checked';
        }

        return CTagHelper::Tag('input', $html_options);
    }

    /**
    * Returns an XHTML compliant <input> CTagHelper::Tag with type="submit".
    * 
    * By default, this helper creates a submit CTagHelper::Tag with a name of <em>commit</em> to avoid
    * conflicts with other parts of the framework.  It is recommended that you do not use the name
    * "submit" for submit tags unless absolutely necessary. Also, the default <i>$value</i> parameter
    * (title of the button) is set to "Save changes", which can be easily overwritten by passing a 
    * <i>$value</i> parameter.
    *
    * <b>Examples:</b>
    * <code>
    *  echo self::SubmitTag();
    * </code>
    *
    * <code>
    *  echo self::SubmitTag('Update Record');
    * </code>
    *
    * @param string $name    field value (title of submit button)
    * @param array  $options additional HTML compliant <input> CTagHelper::Tag parameters
    *
    * @return string XHTML compliant <input> CTagHelper::Tag with type="submit"
    */
    static public function SubmitTag($value = 'Save changes', $options = array())
    {
        return CTagHelper::Tag('input', 
            array_merge(
                array(
                    'type' => 'submit', 
                    'name' => 'commit', 
                    'value' => $value
                ), 
                CUrlHelper::_ConvertOptionsToJavascript(
                CTagHelper::_ConvertOptions($options)
                )
            )
        );
    }

    /**
    * Returns an XHTML compliant <input> CTagHelper::Tag with type="reset".
    *
    * By default, this helper creates a submit CTagHelper::Tag with a name of <em>reset</em>.  Also, the default 
    * <i>$value</i> parameter (title of the button) is set to "Reset" which can be easily overwritten 
    * by passing a <i>$value</i> parameter.
    *
    * <b>Examples:</b>
    * <code>
    *  echo self::RestTag();
    * </code>
    *
    * <code>
    *  echo self::RestTag('Start Over');
    * </code>
    *
    * @param string $name    field value (title of reset button)
    * @param array  $options additional HTML compliant <input> CTagHelper::Tag parameters
    *
    * @return string XHTML compliant <input> CTagHelper::Tag with type="reset"
    */
    static public function RestTag($value = 'Reset', $options = array())
    {
        return CTagHelper::Tag('input', 
            array_merge(
                array(
                    'type' => 'reset', 
                    'name' => 'reset', 
                    'value' => $value
                ), 
                CTagHelper::_ConvertOptions($options)
            ));
    }

    /**
    * Returns an XHTML compliant <input> CTagHelper::Tag with type="image".
    *
    * The self::SubmitImageTag is very similar to the self::SubmitTag, the only difference being that it uses an image
    * for the submit button instead of the browser-generated default button. The image is defined by the 
    * <i>$source</i> parameter and must be a valid image, either local or remote (URL). By default, this 
    * helper creates a submit CTagHelper::Tag with a name of <em>commit</em> to avoid conflicts with other parts of the 
    * framework.  It is recommended that you do not use the name "submit" for submit tags unless absolutely necessary.
    *
    * <b>Examples:</b>
    * <code>
    *  // Assuming your image is in the /web/images/ directory
    *  echo self::SubmitImageTag('my_submit_button.gif');
    * </code>
    *
    * <code>
    *  echo self::SubmitImageTag('http://mydomain.com/my_submit_button.gif');
    * </code>
    *
    * @param string $source  path to image file
    * @param array  $options additional HTML compliant <input> CTagHelper::Tag parameters
    *
    * @return string XHTML compliant <input> CTagHelper::Tag with type="image"
    */
    static public function SubmitImageTag($source, $options = array())
    {
        if (!isset($options['alt']))
        {
            $path_pos = strrpos($source, '/');
            $dot_pos = strrpos($source, '.');
            $begin = $path_pos ? $path_pos + 1 : 0;
            $nb_str = ($dot_pos ? $dot_pos : strlen($source)) - $begin;
            $options['alt'] = ucfirst(substr($source, $begin, $nb_str));
        }

        return CTagHelper::Tag('input', 
            array_merge(
                array(
                    'type' => 'image', 
                    'name' => 'commit', 
                    'src' => CAssetHelper::ImagePath($source)
                ), 
                CUrlHelper::_ConvertOptionsToJavascript(
                    CTagHelper::_ConvertOptions($options)
                )
            )
        );
    }

    /**
    * Returns a <label> CTagHelper::Tag with <i>$label</i> for the specified <i>$id</i> parameter.
    *
    * @param string $id      id
    * @param string $label   label or title
    * @param array  $options additional HTML compliant <label> CTagHelper::Tag parameters
    *
    * @return string <label> CTagHelper::Tag with <i>$label</i> for the specified <i>$id</i> parameter.
    */
    static public function LabelFor($id, $label, $options = array())
    {
        $options = CTagHelper::_ParseAttributes($options);

        if (is_object($label) && method_exists($label, '__toString'))
        {
            $label = $label->__toString();
        }

        return CTagHelper::ContentTag('label', $label, array_merge(
            array('for' => CTagHelper::GetIdFromName($id, null)), $options));
    }

    static public function _ConvertIncludeCustomForSelect($options, &$select_options)
    {
        if (CTagHelper::_GetOption($options, 'include_blank'))
        {
            $select_options[''] = '';
        }
        else if ($include_custom = CTagHelper::_GetOption($options, 'include_custom'))
        {
            $select_options[''] = $include_custom;
        }
    }
}

class HForm extends CFormHelper{}
?>
